package org.gdstash.description;

import org.gdstash.db.*;
import org.gdstash.item.GDItem;
import org.gdstash.item.ItemInfo;
import org.gdstash.util.GDColor;
import org.gdstash.util.GDMsgFormatter;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class DetailComposer {
    public static final String GD_NEWLINE = "{^n}";
    public static final String HTML_HTMLTAG_START = "<html>";
    public static final String HTML_HTMLTAG_END = "</html>";
    public static final String HTML_NEWLINE = "<br>";
    public static final String HTML_LIST_UNSORTED_START = "<ul>";
    public static final String HTML_LIST_UNSORTED_END = "</ul>";
    public static final String HTML_LISTITEM_START = "<li>";
    public static final String HTML_LISTITEM_END = "</li>";
    public static final int DETAIL_ITEM = 1;
    public static final int DETAIL_COMPONENT = 2;
    private static AffixComparator compAffix;
    private static ItemComparator compItem;
    public String preText;
    public List<BonusDetail> bonuses;
    public List<BonusInfoSort> sorted;

    public DetailComposer() {
        this.preText = null;
        this.bonuses = new LinkedList<BonusDetail>();
    }

    static {
        DetailComposer.compAffix = new AffixComparator();
        DetailComposer.compItem = new ItemComparator();
    }

    public static String getComposerBonuses(final DetailComposer comp, final boolean showBase, final boolean showRest, final boolean separateBase) {
        comp.sortItemBonuses();
        boolean inBase = false;
        boolean inOffGlobal = false;
        int offPercent = 0;
        boolean inRetGlobal = false;
        int retPercent = 0;
        String s = null;
        for (final BonusInfoSort sort : comp.sorted) {
            if (!showBase && sort.isBaseStat()) {
                continue;
            }
            if (!showRest && !sort.isBaseStat()) {
                continue;
            }
            if (sort.isBaseStat()) {
                inBase = true;
            } else if (inBase) {
                if (separateBase) {
                    if (s == null) {
                        s = "";
                    } else {
                        s += "<br>";
                    }
                }
                inBase = false;
            }
            if (sort.isGlobal()) {
                if (sort.getInfoType() == 2) {
                    if (!inOffGlobal) {
                        inOffGlobal = true;
                        offPercent = sort.getGlobalOffensePerc();
                        if (offPercent == 0) {
                            continue;
                        }
                        final Object[] args = {offPercent};
                        String title = "";
                        if (sort.isXOR()) {
                            title = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CHANCE_XOR", args);
                        } else {
                            title = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CHANCE_GLOBAL", args);
                        }
                        if (s == null) {
                            s = title + "<br>" + "<ul>";
                        } else {
                            s = s + title + "<br>" + "<ul>";
                        }
                        s = s + "<li>" + sort.info.text + "</li>";
                    } else {
                        if (offPercent == 0) {
                            continue;
                        }
                        if (s == null) {
                            s = "<li>" + sort.info.text + "</li>";
                        } else {
                            s = s + "<li>" + sort.info.text + "</li>";
                        }
                    }
                }
                if (sort.getInfoType() != 3) {
                    continue;
                }
                if (!inRetGlobal) {
                    inRetGlobal = true;
                    retPercent = sort.getGlobalRetaliationPerc();
                    if (retPercent == 0) {
                        continue;
                    }
                    final Object[] args = {retPercent};
                    String title = "";
                    if (sort.isXOR()) {
                        title = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CHANCE_XOR", args);
                    } else {
                        title = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CHANCE_GLOBAL", args);
                    }
                    if (s == null) {
                        s = title + "<br>" + "<ul>";
                    } else {
                        s = s + title + "<br>" + "<ul>";
                    }
                    s = s + "<li>" + sort.info.text + "</li>";
                } else {
                    if (retPercent == 0) {
                        continue;
                    }
                    if (s == null) {
                        s = "<li>" + sort.info.text + "</li>";
                    } else {
                        s = s + "<li>" + sort.info.text + "</li>";
                    }
                }
            } else {
                if (inOffGlobal) {
                    if (offPercent != 0) {
                        if (s == null) {
                            s = "";
                        } else {
                            s += "</ul>";
                        }
                    }
                    inOffGlobal = false;
                }
                if (inRetGlobal) {
                    if (retPercent != 0) {
                        if (s == null) {
                            s = "";
                        } else {
                            s += "</ul>";
                        }
                    }
                    inRetGlobal = false;
                }
                if (s == null) {
                    s = sort.info.text + "<br>";
                } else {
                    s = s + sort.info.text + "<br>";
                }
            }
        }
        if (inOffGlobal && offPercent != 0) {
            if (s == null) {
                s = "";
            } else {
                s += "</ul>";
            }
        }
        if (inRetGlobal && retPercent != 0) {
            if (s == null) {
                s = "";
            } else {
                s += "</ul>";
            }
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    private static String getConversion(final DBItem item) {
        String s = null;
        if (item.getConvertPerc() > 0) {
            final String in = getDamageType(item.getConvertIn());
            final String out = getDamageType(item.getConvertOut());
            if (in != null && out != null) {
                final Object[] args = {item.getConvertPerc(), in, out};
                final String part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CONVERT_FROM_TO", args);
                if (s == null) {
                    s = part + "<br>";
                } else {
                    s = s + part + "<br>";
                }
            }
        }
        if (item.getConvertPerc2() > 0) {
            final String in = getDamageType(item.getConvertIn2());
            final String out = getDamageType(item.getConvertOut2());
            if (in != null && out != null) {
                final Object[] args = {item.getConvertPerc2(), in, out};
                final String part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CONVERT_FROM_TO", args);
                if (s == null) {
                    s = part + "<br>";
                } else {
                    s = s + part + "<br>";
                }
            }
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    private static String getConversion(final DBAffix affix) {
        if (affix == null) {
            return null;
        }
        String s = null;
        if (affix.getConvertPerc() > 0) {
            final String in = getDamageType(affix.getConvertIn());
            final String out = getDamageType(affix.getConvertOut());
            if (in != null && out != null) {
                final Object[] args = {affix.getConvertPerc(), in, out};
                final String part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CONVERT_FROM_TO", args);
                if (s == null) {
                    s = part + "<br>";
                } else {
                    s = s + part + "<br>";
                }
            }
        }
        if (affix.getConvertPerc2() > 0) {
            final String in = getDamageType(affix.getConvertIn2());
            final String out = getDamageType(affix.getConvertOut2());
            if (in != null && out != null) {
                final Object[] args = {affix.getConvertPerc2(), in, out};
                final String part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CONVERT_FROM_TO", args);
                if (s == null) {
                    s = part + "<br>";
                } else {
                    s = s + part + "<br>";
                }
            }
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    private static String getConversion(final DBSkill skill, final DBSkill modifiedSkill, final int level) {
        if (skill == null) {
            return null;
        }
        String s = null;
        if (skill.getConvertPerc(level) > 0) {
            final String in = getDamageType(skill.getConvertIn());
            final String out = getDamageType(skill.getConvertOut());
            if (in != null && out != null) {
                final Object[] args = {skill.getConvertPerc(level), in, out};
                String part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CONVERT_FROM_TO", args);
                if (modifiedSkill != null) {
                    String skillName = modifiedSkill.getName();
                    if (modifiedSkill.getMasteryName() != null) {
                        skillName = skillName + " (" + modifiedSkill.getMasteryName() + ")";
                    }
                    final Object[] args2 = {part, skillName};
                    part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_MODIFIER_TO_SKILL", args2);
                }
                if (s == null) {
                    s = part + "<br>";
                } else {
                    s = s + part + "<br>";
                }
            }
        }
        if (skill.getConvertPerc2(level) > 0) {
            final String in = getDamageType(skill.getConvertIn2());
            final String out = getDamageType(skill.getConvertOut2());
            if (in != null && out != null) {
                final Object[] args = {skill.getConvertPerc2(level), in, out};
                String part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_CONVERT_FROM_TO", args);
                if (modifiedSkill != null) {
                    String skillName = modifiedSkill.getName();
                    if (modifiedSkill.getMasteryName() != null) {
                        skillName = skillName + " (" + modifiedSkill.getMasteryName() + ")";
                    }
                    final Object[] args2 = {part, skillName};
                    part = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_MODIFIER_TO_SKILL", args2);
                }
                if (s == null) {
                    s = part + "<br>";
                } else {
                    s = s + part + "<br>";
                }
            }
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    public static DetailComposer createComposer(final DBStashItem item, final int detailType, final List<DBStat> stats, final List<DBSkillBonus> skillBonuses) {
        final DetailComposer comp = new DetailComposer();
        BonusDetail bonus = null;
        for (final DBStat stat : stats) {
            String prefix = null;
            if (stat != null && stat.isPetStat()) {
                prefix = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_PREFIX_PET", null);
            }
            bonus = new BonusDetail(item, detailType, stat, prefix);
            if (bonus.infos != null && !bonus.infos.isEmpty()) {
                comp.add(bonus);
            }
        }
        if (skillBonuses != null && !skillBonuses.isEmpty()) {
            for (final DBSkillBonus skillBonus : skillBonuses) {
                bonus = skillBonus.getBonusDetail(item);
                if (!bonus.infos.isEmpty()) {
                    comp.add(bonus);
                }
            }
        }
        return comp;
    }

    public static DetailComposer createComposerSkillMod(final DBStashItem item, final int detailType, final List<DBSkillModifier> modifiers) {
        final DetailComposer comp = new DetailComposer();
        BonusDetail bonus = null;
        for (final DBSkillModifier sm : modifiers) {
            final List<DBStat> stats = DBStat.getStatsForLevel(sm.getModifierStatList(), 1);
            for (final DBStat stat : stats) {
                bonus = new BonusDetail(item, detailType, stat, sm);
                if (bonus.infos != null && !bonus.infos.isEmpty()) {
                    comp.add(bonus);
                }
            }
        }
        return comp;
    }

    public static DetailComposer createComposer(final DBStashItem item, final int detailType, final List<DBStat> stats) {
        return createComposer(item, detailType, stats, null);
    }

    public static DetailComposer createComposer(final DBSkill skill, final List<DBStat> stats) {
        final DetailComposer comp = new DetailComposer();
        BonusDetail bonus = null;
        for (final DBStat stat : stats) {
            String prefix = null;
            if (stat != null && stat.isPetStat()) {
                prefix = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_PREFIX_PET", null);
            }
            bonus = new BonusDetail(skill, stat, prefix);
            if (bonus.infos != null && !bonus.infos.isEmpty()) {
                comp.add(bonus);
            }
        }
        return comp;
    }

    private static String getConversion(final DBSkillModifier skillModifier) {
        if (skillModifier == null) {
            return null;
        }
        final DBSkill modifier = skillModifier.getSkillModifier();
        if (modifier == null) {
            return null;
        }
        final DBSkill skill = skillModifier.getSkill();
        if (skill == null) {
            return null;
        }
        String s = getConversion(modifier, skill);
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    private static String getDamageType(final String s) {
        if (s == null) {
            return null;
        }
        String tag = null;
        if (s.equals("Aether")) {
            tag = "OFF_AETHER";
        }
        if (s.equals("Chaos")) {
            tag = "OFF_CHAOS";
        }
        if (s.equals("Cold")) {
            tag = "OFF_COLD";
        }
        if (s.equals("Elemental")) {
            tag = "OFF_ELEMENTAL";
        }
        if (s.equals("Fire")) {
            tag = "OFF_FIRE";
        }
        if (s.equals("Life")) {
            tag = "OFF_LIFE";
        }
        if (s.equals("Lightning")) {
            tag = "OFF_LIGHTNING";
        }
        if (s.equals("Physical")) {
            tag = "OFF_PHYSICAL";
        }
        if (s.equals("Pierce")) {
            tag = "OFF_PIERCE";
        }
        if (s.equals("Poison")) {
            tag = "OFF_POISON";
        }
        if (tag == null) {
            return null;
        }
        return GDMsgFormatter.format(GDMsgFormatter.rbGD, tag, null);
    }

    private static String getSkills(final DBStashItem item, final int detailType, final List<ItemInfo.Skill> list) {
        if (list == null) {
            return null;
        }
        if (list.isEmpty()) {
            return null;
        }
        if (item != null) {
            if (detailType == 1 && item.isComponent() && item.isIncompleteComponent()) {
                return null;
            }
            if (detailType == 2) {
                if (item.getDBComponent() == null) {
                    return null;
                }
                if (item.isIncompleteComponent()) {
                    return null;
                }
            }
        }
        String s = null;
        for (final ItemInfo.Skill iis : list) {
            final DBSkill skill = iis.getSkill();
            final String text = getSkillDescription(item, skill, iis.getLevel(), iis.getController());
            if (s == null) {
                s = text;
            } else {
                if (text == null) {
                    continue;
                }
                s += text;
            }
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    public static String getSkillDescription(final DBSkill skill, final int level, final int charLevel, final boolean nextLevel, final boolean fullHTML) {
        if (skill == null) {
            return null;
        }
        String s = "";
        if (fullHTML) {
            s = "<html>";
        }
        String text = null;
        text = skill.getName();
        if (text != null) {
            s = s + GDColor.HTML_COLOR_SKILL + text + "</font>" + "<br>";
        }
        text = skill.getDescription();
        if (text != null) {
            s = s + text + "<br>";
        }
        if (level > 0) {
            if (fullHTML) {
                s += "<br>";
                int value = 0;
                value = skill.getManaCost(level);
                if (value > 0) {
                    final Object[] iArgs = {value};
                    text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_ENERGY_COST", iArgs);
                    s = s + text + "<br>";
                }
                final float fVal = skill.getCooldownTime(level);
                if (fVal > 0.0) {
                    final Object[] fArgs = {fVal};
                    text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_COOLDOWN_TIME", fArgs);
                    s = s + text + "<br>";
                }
                value = skill.getActiveDuration(level);
                if (value > 0) {
                    final Object[] iArgs2 = {value};
                    text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_ACTIVE_DURATION", iArgs2);
                    s = s + text + "<br>";
                }
            }
            text = getSkillLevelStatDescription(skill, level, DBSkill.SkillDetail.SKILL, false);
            final DBSkill petSkill = skill.getPetSkill();
            if (petSkill != null) {
                final String petText = getSkillLevelStatDescription(petSkill, level, DBSkill.SkillDetail.PET, true);
                if (petText != null) {
                    final String line = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "TXT_PET_BONUS");
                    if (text == null) {
                        text = GDColor.HTML_COLOR_SKILL + line + "</font>" + "<br>" + petText;
                    } else {
                        text = text + "<br>" + GDColor.HTML_COLOR_SKILL + line + "</font>" + "<br>" + petText;
                    }
                }
            }
            if (text != null) {
                String lvl = "";
                if (fullHTML) {
                    final Object[] iArgs2 = {level};
                    lvl = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_SKILL_LEVEL", iArgs2);
                    lvl = GDColor.HTML_COLOR_SKILL + lvl + "</font>" + "<br>";
                }
                s = s + "<br>" + lvl + text;
            }
            final DBPet pet = skill.getPet(level);
            if (pet != null) {
                pet.setPlayerLevel(charLevel);
                s = s + "<br>" + pet.getPetDescription(false);
            }
        }
        if (nextLevel && skill.getMaxLevel() > level) {
            s += "<br>";
            text = getSkillLevelStatDescription(skill, level + 1, DBSkill.SkillDetail.SKILL, false);
            final DBSkill petSkill = skill.getPetSkill();
            if (petSkill != null) {
                final String petText = getSkillLevelStatDescription(petSkill, level + 1, DBSkill.SkillDetail.PET, true);
                if (petText != null) {
                    final String line = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "TXT_PET_BONUS");
                    if (text == null) {
                        text = GDColor.HTML_COLOR_SKILL + line + "</font>" + "<br>" + petText;
                    } else {
                        text = text + "<br>" + GDColor.HTML_COLOR_SKILL + line + "</font>" + "<br>" + petText;
                    }
                }
            }
            if (text != null) {
                final String lvl = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "TXT_SKILL_NEXT_LEVEL");
                s = s + "<br>" + GDColor.HTML_COLOR_SKILL + lvl + "</font>" + "<br>";
                s = s + text + "<br>";
            }
            final DBPet pet = skill.getPet(level + 1);
            if (pet != null) {
                pet.setPlayerLevel(charLevel);
                s = s + "<br>" + pet.getPetDescription(false) + "<br>";
            }
        }
        if (fullHTML) {
            s += "</html>";
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    private static String getConversion(final DBSkill skill, final DBSkill modifiedSkill) {
        return getConversion(skill, modifiedSkill, 1);
    }

    public static String getSkillLevelStatDescription(final DBSkill skill, final int level, final DBSkill.SkillDetail detail, final boolean petSkill) {
        if (skill == null) {
            return null;
        }
        if (level == 0) {
            return null;
        }
        String s = "";
        String part = getConversion(skill, null, level);
        if (part != null) {
            s += part;
        }
        final String[] exclude = {"skillCooldownTime", "skillActiveDuration", "skillMaxLevel"};
        final List<DBStat> stats = DBStat.getByLevel(skill.getStatList(exclude, detail, petSkill), level);
        final DetailComposer comp = createComposer(skill, stats);
        part = getComposerBonuses(comp, true, true, true);
        if (part != null) {
            s += part;
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    public static String getPetDescription(final DBPet pet, final boolean fullHTML) {
        if (pet == null) {
            return null;
        }
        String s = "";
        if (fullHTML) {
            s = "<html>";
        }
        String text = null;
        text = pet.getName();
        s = s + GDColor.HTML_COLOR_SKILL + text + "</font>" + "<br>";
        int value = 0;
        value = pet.getHealth();
        if (value > 0) {
            final Object[] iArgs = {value};
            text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_PET_HEALTH", iArgs);
            s = s + text + "<br>";
        }
        value = pet.getMana();
        if (value > 0) {
            final Object[] iArgs = {value};
            text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_PET_MANA", iArgs);
            s = s + text + "<br>";
        }
        s += "<br>";
        text = pet.getPetSkillDescriptions();
        if (text != null && !text.isEmpty()) {
            s += text;
        }
        if (fullHTML) {
            s += "</html>";
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    public static String getSkillDescription(final DBStashItem item, final DBSkill skill, final int level, final DBController dbController) {
        if (skill == null) {
            return null;
        }
        String s = null;
        String text = null;
        if (skill.getName() == null) {
            text = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "TXT_PET_BONUS");
        } else {
            final Object[] args = {skill.getName()};
            text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_SKILL_GRANTED", args);
        }
        if (s == null) {
            s = GDColor.HTML_COLOR_SKILL + text + "</font>" + "<br>";
        } else {
            s = s + GDColor.HTML_COLOR_SKILL + text + "</font>" + "<br>";
        }
        if (skill.getDescription() != null) {
            s = s + skill.getDescription() + "<br>" + "<br>";
        }
        int value = 0;
        value = skill.getTriggerLifePerc(level);
        if (value > 0) {
            final Object[] iArgs = {value};
            text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_TRIGGER_HEALTH", iArgs);
            s = s + text + "<br>";
        } else if (dbController != null) {
            final String trigger = dbController.getTriggerType();
            if (trigger != null) {
                value = dbController.getTriggerChance();
                if (value > 0) {
                    text = DBController.getTriggerDescription(trigger, value);
                    if (text != null) {
                        s = s + text + "<br>";
                    }
                }
            }
        }
        value = skill.getManaCost(level);
        if (value > 0) {
            final Object[] iArgs = {value};
            text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_ENERGY_COST", iArgs);
            s = s + text + "<br>";
        }
        final float fVal = skill.getCooldownTime(level);
        if (fVal > 0.0) {
            final Object[] fArgs = {fVal};
            text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_COOLDOWN_TIME", fArgs);
            s = s + text + "<br>";
        }
        value = skill.getActiveDuration(level);
        if (value > 0) {
            final Object[] iArgs2 = {value};
            text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_ACTIVE_DURATION", iArgs2);
            s = s + text + "<br>";
        }
        final String[] exclude = {"skillCooldownTime", "skillActiveDuration", "skillMaxLevel"};
        int skillLevel = skill.getSkillLevel();
        if (skillLevel == 0) {
            skillLevel = level;
        }
        final List<DBStat> stats = DBStat.getByLevel(skill.getStatList(exclude, DBSkill.SkillDetail.ALL), skillLevel);
        final DetailComposer comp = createComposer(item, 1, stats);
        final String part = getComposerBonuses(comp, true, true, true);
        if (part != null) {
            s += part;
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    private static String getItemReqs(final GDItem item) {
        String s = null;
        int value = 0;
        value = item.getRequiredLevel();
        if (value > 0) {
            final Object[] args = {value};
            final String text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_REQ_LEVEL", args);
            if (s == null) {
                s = text + "<br>";
            } else {
                s = s + text + "<br>";
            }
        }
        value = item.getRequiredPhysique();
        if (value > 0) {
            final Object[] args = {value};
            final String text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_REQ_PHYSIQUE", args);
            if (s == null) {
                s = text + "<br>";
            } else {
                s = s + text + "<br>";
            }
        }
        value = item.getRequiredCunning();
        if (value > 0) {
            final Object[] args = {value};
            final String text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_REQ_CUNNING", args);
            if (s == null) {
                s = text + "<br>";
            } else {
                s = s + text + "<br>";
            }
        }
        value = item.getRequiredSpirit();
        if (value > 0) {
            final Object[] args = {value};
            final String text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_REQ_SPIRIT", args);
            if (s == null) {
                s = text + "<br>";
            } else {
                s = s + text + "<br>";
            }
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    public static String getSkillLevelStatDescription(final DBSkill skill, final int level, final DBSkill.SkillDetail detail) {
        return getSkillLevelStatDescription(skill, level, detail, false);
    }

    public static String getItemText(final GDItem item, final ItemInfo info) {
        final DetailComposer compItem = createComposer(item.getDBStashItem(), 1, info.itemStats, info.itemSkillBonuses);
        final DetailComposer compSkillModifier = createComposerSkillMod(item.getDBStashItem(), 1, info.skillModifiers);
        final DetailComposer compPet = createComposer(item.getDBStashItem(), 1, info.petStats);
        final DetailComposer compComponent = createComposer(item.getDBStashItem(), 2, info.componentStats, info.componentSkillBonuses);
        final DetailComposer compCompletion = createComposer(item.getDBStashItem(), 1, info.completionStats, info.completionSkillBonuses);
        final DetailComposer compEnchantment = createComposer(item.getDBStashItem(), 1, info.enchantmentStats);
        boolean fCompletion = false;
        String s = "<html>";
        String part = null;
        part = item.getItemCategory();
        if (part != null) {
            s = s + part + "<br>" + "<br>";
        }
        if (item.isElixir() || item.isFactionBooster() || item.isNote() || item.isPotion() || item.isQuestItem()) {
            s = s + item.getItemDescription() + "<br>";
        }
        boolean base = false;
        part = getComposerBonuses(compItem, true, false, true);
        if (part != null) {
            s += part;
            base = true;
        }
        part = getConversion(item.getDBItem());
        if (part != null) {
            s += part;
            base = true;
        }
        part = getConversion(item.getPrefix());
        if (part != null) {
            s += part;
            base = true;
        }
        part = getConversion(item.getSuffix());
        if (part != null) {
            s += part;
            base = true;
        }
        if (base) {
            s += "<br>";
        }
        part = getComposerBonuses(compItem, false, true, true);
        if (part != null) {
            s += part;
        }
        for (final DBSkillModifier sm : info.skillModifiers) {
            part = getConversion(sm);
            if (part != null) {
                s += part;
            }
        }
        part = getComposerBonuses(compSkillModifier, true, true, false);
        if (part != null) {
            s += part;
        }
        part = getComposerBonuses(compPet, true, true, true);
        if (part != null) {
            final String text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_PET_BONUS", null);
            s = s + "<br>" + text + "<br>" + part;
        }
        if (item.getPlusAllSkills() > 0) {
            final Object[] args = {item.getPlusAllSkills()};
            final String text2 = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_PLUS_ALLSKILLS", args);
            s = s + text2 + "<br>";
        }
        part = getSkills(item.getDBStashItem(), 1, info.itemSkills);
        if (part != null) {
            s = s + "<br>" + part;
        }
        part = getSkills(item.getDBStashItem(), 1, info.petSkills);
        if (part != null) {
            s = s + "<br>" + part;
        }
        part = getComposerBonuses(compComponent, true, true, false);
        if (part != null) {
            final String text = GDColor.HTML_COLOR_COMPONENT + item.getComponentName() + "</font>";
            s = s + "<br>" + text + "<br>" + part;
        }
        part = getSkills(item.getDBStashItem(), 2, info.componentSkills);
        if (part != null) {
            s = s + "<br>" + part;
        }
        if (!item.isComponent() && item.getComponentName() == null) {
            part = getComposerBonuses(compCompletion, true, true, true);
            if (part != null && !fCompletion) {
                String text = GDMsgFormatter.format(GDMsgFormatter.rbGD, "TXT_COMPLETION_BONUS", null);
                text = GDColor.HTML_COLOR_ITEM_TYPE + text + "</font>";
                s = s + "<br>" + text + "<br>" + part;
                fCompletion = true;
            }
        }
        part = getComposerBonuses(compEnchantment, true, true, false);
        if (part != null) {
            final String text = GDColor.HTML_COLOR_ENCHANTMENT + item.getEnchantmentName() + "</font>";
            s = s + "<br>" + text + "<br>" + part;
        }
        part = item.getItemSetBonuses();
        if (part != null) {
            s = s + "<br>" + part;
        }
        part = getItemReqs(item);
        if (part != null) {
            s = s + "<br>" + part;
        }
        s += "</html>";
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    public void add(final BonusDetail bonus) {
        if (bonus == null) {
            return;
        }
        if (bonus.infos == null) {
            return;
        }
        if (bonus.infos.size() > 0) {
            this.bonuses.add(bonus);
        }
    }

    public void add(final DetailComposer composer) {
        if (composer == null) {
            return;
        }
        if (composer.bonuses == null) {
            return;
        }
        if (composer.bonuses.size() > 0) {
            this.bonuses.addAll(composer.bonuses);
        }
    }

    private void sortAffixBonuses() {
        this.sorted = new LinkedList<BonusInfoSort>();
        BonusInfoSort sort = null;
        for (final BonusDetail bonus : this.bonuses) {
            for (final BonusInfo info : bonus.infos) {
                sort = new BonusInfoSort(bonus, info);
                this.sorted.add(sort);
            }
        }
        Collections.<BonusInfoSort>sort(this.sorted, DetailComposer.compAffix);
    }

    public String getAffixText() {
        this.sortAffixBonuses();
        String s = this.preText;
        boolean separator;
        boolean bracket = separator = (this.preText.indexOf("[") != -1);
        int count = 0;
        for (final BonusInfoSort sort : this.sorted) {
            if (count > 0) {
                if (sort.info.dmgChance) {
                    continue;
                }
                if (sort.info.dmgGlobal) {
                    continue;
                }
                if (sort.info.infoType == 3) {
                    continue;
                }
            }
            if (sort.info.dmgDuration && count > 1) {
                continue;
            }
            if (!bracket) {
                if (s == null) {
                    s = "[";
                } else {
                    s += " [";
                }
                bracket = true;
            }
            if (separator) {
                s += ". ";
            } else {
                separator = true;
            }
            s += sort.info.text;
            ++count;
            if (count >= 4) {
                break;
            }
        }
        if (bracket) {
            s += "]";
        }
        return s;
    }

    private void sortItemBonuses() {
        this.sorted = new LinkedList<BonusInfoSort>();
        BonusInfoSort sort = null;
        for (final BonusDetail bonus : this.bonuses) {
            for (final BonusInfo info : bonus.infos) {
                sort = new BonusInfoSort(bonus, info);
                this.sorted.add(sort);
            }
        }
        Collections.<BonusInfoSort>sort(this.sorted, DetailComposer.compItem);
    }
}
